
/*
 * @(#)MaintWorkflowScheduleTriggerAction.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.workflow.web;

import java.sql.Connection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.quartz.SchedulerException;

import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.ErrorConstant;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemFunctionConstant;
import com.dcivision.framework.TextUtility;
import com.dcivision.framework.Utility;
import com.dcivision.framework.web.AbstractActionForm;
import com.dcivision.workflow.WorkflowErrorConstant;
import com.dcivision.workflow.bean.WorkflowGroup;
import com.dcivision.workflow.bean.WorkflowRecord;
import com.dcivision.workflow.bean.WorkflowSchedule;
import com.dcivision.workflow.bean.WorkflowScheduleTriggers;
import com.dcivision.workflow.core.SystemWorkflowConstant;
import com.dcivision.workflow.core.TriggerParser;
import com.dcivision.workflow.dao.WorkflowGroupDAObject;
import com.dcivision.workflow.dao.WorkflowRecordDAObject;
import com.dcivision.workflow.dao.WorkflowScheduleTriggersDAObject;

/**
 * MaintWorkflowScheduleTriggerAction.java
 * @author      Tony Chen
 * @company     DCIVision Limited
 * @creation date   23/12/2004
 * @version     $Revision: 1.11.2.2 $
 */


public class MaintWorkflowScheduleTriggerAction extends com.dcivision.framework.web.AbstractMaintAction {

  public static final String REVISION = "$Revision: 1.11.2.2 $";


  /** Creates a new instance of MaintWorkflowScheduleTriggerAction */
  public MaintWorkflowScheduleTriggerAction() {
    super();
  }

  /** getFunctionCode
   *
   * Abstract function which sub-class should implement to return the corresponding
   * function code.
   *
   * @return   The function code
   */
  public String getFunctionCode() {
    return SystemFunctionConstant.WORKFLOW_SCHEDULE;
  }

  /** getMajorDAOClassName
   *
   * Abstract function which sub-class should implement to return the corresponding
   * major DAO class name used in this class.
   *
   * @return   The DAO class name
   */
  public String getMajorDAOClassName() {
    return "com.dcivision.workflow.dao.WorkflowScheduleTriggersDAObject";
  }

  /**
   * setPageTitle
   * set the extend page title and page path.
   * default page path/title will be created by navmode and functionCode
   * @throws ApplicationException
   * @throws NumberFormatException
   */
  public void setPageTitle(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping, ActionForward actionForward) throws NumberFormatException, ApplicationException{
    MaintWorkflowScheduleTriggerForm maintWorkflowScheduleTriggerForm = (MaintWorkflowScheduleTriggerForm)form;
    StringBuffer extendPath = new StringBuffer("<a href=\"ListWorkflowScheduleTrigger.do?workflowGroupID=");
    extendPath.append(maintWorkflowScheduleTriggerForm.getWorkflowGroupID());
    extendPath.append("&opMode=");
    extendPath.append(GlobalConstant.OP_MODE_LIST_RETAIN);
    extendPath.append("\">");
    WorkflowGroup currentGroup = (WorkflowGroup)(new WorkflowGroupDAObject(this.getSessionContainer(request),this.getConnection(request)).getObjectByID(Integer.valueOf(maintWorkflowScheduleTriggerForm.getWorkflowGroupID())));
    extendPath.append(currentGroup.getGroupName());
    extendPath.append("</a>");
    request.setAttribute(GlobalConstant.EXTEND_PAGE_PATH, extendPath.toString());
  }

  public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException {
    AbstractActionForm maintForm = (AbstractActionForm)form;

    String opMode = (maintForm != null) ? maintForm.getOpMode() : request.getParameter("opMode"); //maintForm.getOpMode();
    String navMode = (maintForm != null) ? maintForm.getNavMode() : request.getParameter("navMode"); //maintForm.getNavMode();

    String workflowGroupID = request.getParameter("workflowGroupID");

    //get workflow released record List under the worklowGroupID group.

    WorkflowRecordDAObject wfRecordDAO = new WorkflowRecordDAObject(this.getSessionContainer(request), this.getConnection(request));
    List wfRecordList = new ArrayList();
    if ( !Utility.isEmpty(workflowGroupID) ){
    	wfRecordList = wfRecordDAO.getListByWorkflowGroupIDWorkflowCategoryIDWorkflowRecordStatus(new Integer(workflowGroupID), new Integer(SystemWorkflowConstant.SYS_GENERAL_WORKFLOW), WorkflowRecord.WORKFLOW_RECORD_RELEASE,GlobalConstant.TRUE );
    }
    request.setAttribute("workflowRecordList", wfRecordList);
    ActionForward forword=super.execute(mapping, form, request, response);
    if (!isError(request)) {
      if (!Utility.isEmpty(opMode)
          && (GlobalConstant.OP_MODE_UPDATE.equals(opMode) || GlobalConstant.OP_MODE_INSERT
              .equals(opMode))) {
        this.selectRecord(mapping, maintForm, request, response);
      }
    }
    return forword;

  }

  public void refreshFormData(
        ActionMapping mapping,
        AbstractActionForm form,
        HttpServletRequest request,
        HttpServletResponse response)
  throws ApplicationException {
    List formElementList = new ArrayList();

    try {
      SessionContainer sessionContainer = this.getSessionContainer(request);
      Connection conn = this.getConnection(request);

    } catch (Exception e) {
      log.error(e, e);
      throw new ApplicationException(ErrorConstant.COMMON_FATAL_ERROR, e);
    } finally {
    }
  }

  public void insertRecord(ActionMapping mapping, AbstractActionForm form, HttpServletRequest request, HttpServletResponse response) throws ApplicationException {
    WorkflowScheduleTriggers trigger = new WorkflowScheduleTriggers();
    WorkflowScheduleTriggersDAObject triggerDAO = new WorkflowScheduleTriggersDAObject(this.getSessionContainer(request), this.getConnection(request));

    trigger = this.getScheduleTrigger(form, request);
    trigger = (WorkflowScheduleTriggers)triggerDAO.insertObject(trigger);
    form.setID(String.valueOf(trigger.getID()));

    //create schedule job
    this.createScheduleJob(trigger);

  }

  public void updateRecord(ActionMapping mapping, AbstractActionForm form, HttpServletRequest request, HttpServletResponse response) throws ApplicationException {
    WorkflowScheduleTriggersDAObject triggerDAO = new WorkflowScheduleTriggersDAObject(this.getSessionContainer(request), this.getConnection(request));
    WorkflowScheduleTriggers trigger = this.getScheduleTrigger(form, request);
    trigger = (WorkflowScheduleTriggers)triggerDAO.updateObject(trigger);

    //create schedule job
    this.createScheduleJob(trigger);
  }

  public void selectRecord(ActionMapping mapping, AbstractActionForm form, HttpServletRequest request, HttpServletResponse response) throws ApplicationException {
    WorkflowScheduleTriggersDAObject triggerDAO = (WorkflowScheduleTriggersDAObject)this.getMajorDAObject(request);
    MaintWorkflowScheduleTriggerForm maintForm = (MaintWorkflowScheduleTriggerForm)form;

    WorkflowScheduleTriggers trigger = (WorkflowScheduleTriggers)triggerDAO.getObjectByID(TextUtility.parseIntegerObj(maintForm.getID()));

    maintForm.setFormData(trigger);

    //set the Start Date & Start Time
    Timestamp scheduleStart = trigger.getScheduleStart();
    Calendar startCal = Utility.timestampToCalendar(scheduleStart);
    maintForm.setTriggerHour( startCal.get(Calendar.HOUR_OF_DAY) );
    maintForm.setTriggerMinute( startCal.get(Calendar.MINUTE) );

    maintForm.setStartDate( TextUtility.formatDate(scheduleStart,"yyyy-MM-dd") );

    //set the Start Time
    Integer startTime = trigger.getStartTime();
    int triggerHour = (startTime.intValue() / (60 * 60 * 1000) );
    int triggerMinute = ( (startTime.intValue() - triggerHour * 60 * 60 * 1000) / (60 * 1000) );

    maintForm.setTriggerHour( triggerHour  );
    maintForm.setTriggerMinute( triggerMinute );

    //set the End Date
    Timestamp scheduleEnd = trigger.getScheduleEnd();
    if ( Utility.isEmpty(scheduleEnd) ){
      if ( Utility.isEmpty( trigger.getRecurrMaxRun() ) ) {
        maintForm.setEndStyle( String.valueOf(WorkflowSchedule.END_STYLE_NONE) );
      } else {
        maintForm.setEndStyle( String.valueOf(WorkflowSchedule.END_STYLE_COUNT) );
        maintForm.setMaxTrigger( trigger.getRecurrMaxRun().toString() );
      }
    } else {
      maintForm.setEndStyle( String.valueOf(WorkflowSchedule.END_STYLE_DATE) );
      maintForm.setEndDate( TextUtility.formatDate(scheduleEnd,"yyyy-MM-dd") );
    }

    //set the Schedule Type
    maintForm.setScheduleType( trigger.getScheduleType() );

    //set the Schedule Style
    //======= Daily =======
    if ( WorkflowSchedule.TRIGGER_TYPE_DAILY == trigger.getScheduleType().intValue() ){ // Daily
      maintForm.setDailyStyle( trigger.getScheduleStyle().toString() );
      //set the interval N days
      if ( WorkflowSchedule.DAILY_STYLE_INTERVAL == trigger.getScheduleStyle().intValue() ){
        maintForm.setIntervalDay( trigger.getExtraParameter1() );
      }

    //======= Weekly =======
    } else if ( WorkflowSchedule.TRIGGER_TYPE_WEEKLY == trigger.getScheduleType().intValue() ){ // Weekly
      maintForm.setWeeklyStyle( trigger.getScheduleStyle().toString() );
      //set the interval N weeks
      if ( WorkflowSchedule.WEEKLY_STYLE_INTERVAL == trigger.getScheduleStyle().intValue() ){
        maintForm.setIntervalWeek(trigger.getExtraParameter1());
      }

      //set the included list
      String includedList = trigger.getIncludeList();
      if ( !Utility.isEmpty(includedList) ) {
        String[] daysOfWeek = TextUtility.splitString(includedList, WorkflowSchedule.EXPR_SPECIAL_ADDITIONAL);
        maintForm.setDaysOfWeek( daysOfWeek );
      }

    // ======= Monthly =======
    } else if ( WorkflowSchedule.TRIGGER_TYPE_MONTHLY == trigger.getScheduleType().intValue() ){ // Monthly
      maintForm.setMonthlyStyle( trigger.getScheduleStyle().toString() );
      //set the Day Of Month
      if( WorkflowSchedule.MONHTLY_STYLE_DAY_OF_MONTH == trigger.getScheduleStyle().intValue() ){
        //set the Day Of Month
        maintForm.setDayOfMonth( trigger.getExtraParameter1() );

      //get the Week Of Month
      } else {
        //set the Week Of Month = weekOfMonth + dayOfWeek
        maintForm.setWeekOfMonth( trigger.getExtraParameter1() );
        maintForm.setDayOfWeek( trigger.getExtraParameter2() );
      }
      //set the included list
      String includedList = trigger.getIncludeList();
      if ( !Utility.isEmpty(includedList) ) {
        String[] monthsOfYear = TextUtility.splitString(includedList, WorkflowSchedule.EXPR_SPECIAL_ADDITIONAL);
        maintForm.setMonthsOfYear( monthsOfYear );
      }
    }


  }

  /**
   * get the schedule trigger information from the Maint Form
   * @param form
   * @param request
   * @return
   * @throws ApplicationException
   */
  private WorkflowScheduleTriggers getScheduleTrigger(
        AbstractActionForm form,
        HttpServletRequest request) throws ApplicationException {
    MaintWorkflowScheduleTriggerForm maintForm = (MaintWorkflowScheduleTriggerForm)form;
    WorkflowScheduleTriggersDAObject triggerDAO = new WorkflowScheduleTriggersDAObject(this.getSessionContainer(request), this.getConnection(request));
    WorkflowScheduleTriggers trigger = new WorkflowScheduleTriggers();

    if(GlobalConstant.NAV_MODE_EDIT.equals(form.getNavMode())){
      trigger = (WorkflowScheduleTriggers)triggerDAO.getObjectByID(new Integer(form.getID()));
    } else {
      trigger = (WorkflowScheduleTriggers)maintForm.getFormData();
    }

    //get Schedule Name
    trigger.setScheduleName(maintForm.getScheduleName());
    //get Workflow Record ID
    trigger.setWorkflowRecordID(maintForm.getWorkflowRecordID());
    //get Criteria Expression
    trigger.setCriteriaExpression(maintForm.getCriteriaExpression());
    //get Criteria Config
    trigger.setCriteriaConfig(maintForm.getCriteriaConfig());
    //get Status
    trigger.setStatus(maintForm.getStatus());

    //get the Schedule_Start
    Timestamp scheduleStart = TextUtility.parseDBDateTimeToTimeStamp(maintForm.getStartDate());
    trigger.setScheduleStart(scheduleStart);

    //get the Start_Time
    int triggerHour = maintForm.getTriggerHour();
    int triggerMinute = maintForm.getTriggerMinute();
    int startTime = 0;

    Calendar startCal = Utility.timestampToCalendar(scheduleStart);

    if ( triggerHour > -1 ){
      startTime = triggerHour * 60 * 60 * 1000;
      startCal.add(Calendar.HOUR, triggerHour);
    }

    if ( triggerMinute > -1 ){
      startTime += triggerMinute * 60 * 1000;
      startCal.add(Calendar.MINUTE, triggerMinute);
    }

    trigger.setStartTime(new Integer(startTime) );
    trigger.setScheduleStart(Utility.calendarToTimestamp(startCal) );

    //get the Schedule_End
    if ( !Utility.isEmpty(maintForm.getEndDate()) ){
      Timestamp endDateTime = TextUtility.parseDBDateTimeToTimeStamp(maintForm.getEndDate());
      Calendar endDateCal = Utility.timestampToCalendar(endDateTime);
      endDateCal.add(Calendar.HOUR_OF_DAY,23);
      endDateCal.add(Calendar.MINUTE, 59);
      trigger.setScheduleEnd(Utility.calendarToTimestamp(endDateCal));
    }

    //get the Schedule_Style
    // ======= Daily =======
    if ( WorkflowSchedule.TRIGGER_TYPE_DAILY == maintForm.getScheduleType().intValue() ){ // Daily
      trigger.setScheduleStyle(TextUtility.parseIntegerObj(maintForm.getDailyStyle()));
      //set the interval N days
      if ( WorkflowSchedule.DAILY_STYLE_INTERVAL == TextUtility.parseInteger(maintForm.getDailyStyle()) ){
        trigger.setExtraParameter1(maintForm.getIntervalDay());
      }

    //======= Weekly =======
    } else if ( WorkflowSchedule.TRIGGER_TYPE_WEEKLY == maintForm.getScheduleType().intValue() ){ // Weekly
      trigger.setScheduleStyle(TextUtility.parseIntegerObj(maintForm.getWeeklyStyle()));
      //set the interval N weeks
      trigger.setExtraParameter1(maintForm.getIntervalWeek());
      //set the included list
      String[] daysOfWeek = maintForm.getDaysOfWeek();
      String includeList = "";
      for ( int i = 0; !Utility.isEmpty(daysOfWeek) && i < daysOfWeek.length; i++ ){
        if( i > 0 ){
          includeList += WorkflowSchedule.EXPR_SPECIAL_ADDITIONAL;
        }
        includeList += daysOfWeek[i];
      }
      if ( !Utility.isEmpty(includeList) ){
        trigger.setIncludeList(includeList);
      }
      log.debug("======= includeList ======"+includeList);

    // ======= Monthly =======
    } else if ( WorkflowSchedule.TRIGGER_TYPE_MONTHLY == maintForm.getScheduleType().intValue() ){ // Monthly
      trigger.setScheduleStyle(TextUtility.parseIntegerObj(maintForm.getMonthlyStyle()));
      //get the Day Of Month
      if( WorkflowSchedule.MONHTLY_STYLE_DAY_OF_MONTH == TextUtility.parseInteger(maintForm.getMonthlyStyle())  ){
        //set the Day Of Month
        trigger.setExtraParameter1( maintForm.getDayOfMonth() );

      //get the Week Of Month
      } else {
        //set the Week Of Month = weekOfMonth + dayOfWeek
        trigger.setExtraParameter1( maintForm.getWeekOfMonth() );
        trigger.setExtraParameter2( maintForm.getDayOfWeek() );
      }

      //set the included list
      String[] monthsOfYear = maintForm.getMonthsOfYear();
      String includeList = "";
      for (int i = 0; !Utility.isEmpty(monthsOfYear) && i < monthsOfYear.length; i++) {
        if (i > 0) {
          includeList += WorkflowSchedule.EXPR_SPECIAL_ADDITIONAL;
        }
        includeList += monthsOfYear[i];
      }
      if (!Utility.isEmpty(includeList)) {
        trigger.setIncludeList(includeList);
      }
      log.debug("======= includeList ======"+includeList);

    }

    //get the recurr_max_run
    if ( String.valueOf(WorkflowSchedule.END_STYLE_COUNT).equals(maintForm.getEndStyle()) &&
         !Utility.isEmpty( maintForm.getMaxTrigger()) ) {
       trigger.setRecurrMaxRun( new Integer(maintForm.getMaxTrigger()) );
    }

    this.checkScheduleName(trigger, request);
    return trigger;
  }

  /**
   * check duplicate schedule name
   * @param trigger
   * @param request
   * @throws ApplicationException
   */
  private void checkScheduleName(WorkflowScheduleTriggers trigger, HttpServletRequest request) throws ApplicationException{

    WorkflowScheduleTriggersDAObject triggerDAO = (WorkflowScheduleTriggersDAObject) this.getMajorDAObject(request);

    WorkflowScheduleTriggers oldTrigger = null;
    try {
      oldTrigger = (WorkflowScheduleTriggers) triggerDAO.getObjectByScheduleName(trigger.getScheduleName());
    } catch (ApplicationException ae) {
      if ( !ErrorConstant.DB_RECORD_NOT_FOUND_ERROR.equals(ae.getMsgCode()) ){
        throw ae;
      }
    }

    //update
    if ( !Utility.isEmpty(trigger.getID()) ){
      if ( !Utility.isEmpty(oldTrigger) && !trigger.getID().equals(oldTrigger.getID()) ) {
        throw new ApplicationException(WorkflowErrorConstant.SCHEDULE_NAME_DUPLICATE);
      }
    //insert
    } else {
      if ( !Utility.isEmpty(oldTrigger) ){
        throw new ApplicationException(WorkflowErrorConstant.SCHEDULE_NAME_DUPLICATE);
      }
    }
  }

  /**
   * create schedule job according to workflow schedule trigger.
   * @param scheduleTrigger
   * @throws ApplicationException
   */

  private void createScheduleJob(WorkflowScheduleTriggers scheduleTrigger) throws ApplicationException{
    log.debug("===== Status ======" + scheduleTrigger.getStatus());
    if ( WorkflowSchedule.SCHEDULE_TRIGGER_RELEASED_STATUS.equals(scheduleTrigger.getStatus()) ) {
      WorkflowSchedule workflowSchedule = scheduleTrigger.getWorkflowSchedule();

      Timestamp targetSrcTime = null;
      org.quartz.Trigger trigger = null;

      try {
        log.info("******** Trigger Create Begin **********");
        org.quartz.Scheduler sched = com.dcivision.framework.SchedulerFactory.getScheduler(); ;

        org.quartz.JobDataMap dataMap = new org.quartz.JobDataMap();
        dataMap.put("objectID", scheduleTrigger.getID().toString());
        log.debug("===== objectID ======" + scheduleTrigger.getID());

        org.quartz.JobDetail job = new org.quartz.JobDetail(
          GlobalConstant.JOB_PREFIX_WORKFLOW_SCHEDULE + workflowSchedule.getID(),
          GlobalConstant.JOB_PREFIX_WORKFLOW_SCHEDULE + "GROUP",
          com.dcivision.workflow.core.WorkflowTriggerScheduleJob.class);

        job.setJobDataMap(dataMap);

        trigger = TriggerParser.parse(workflowSchedule);

        trigger.setMisfireInstruction(org.quartz.SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);

        java.util.Date ft = sched.scheduleJob(job, trigger);
        sched = null;
        log.info("******** Trigger Create End   **********");
      } catch (Exception e) {
        log.error("Error in Scheduler.", e);
        throw new ApplicationException(ErrorConstant.SYS_SCHEDULER_ERROR);
      }
    }

  }

  public void deleteRecord(ActionMapping mapping, AbstractActionForm form, HttpServletRequest request, HttpServletResponse response) throws ApplicationException {
    super.deleteRecord(mapping, form, request, response);
    //Cancel the Schedule Job
    try {
      log.debug("=== Cancel Schedule_Job ===");
      org.quartz.Scheduler sched = com.dcivision.framework.SchedulerFactory.getScheduler();

      log.debug("JOB:"+GlobalConstant.JOB_PREFIX_WORKFLOW_SCHEDULE + form.getID() +":" +
        GlobalConstant.JOB_PREFIX_WORKFLOW_SCHEDULE + "GROUP");

      sched.unscheduleJob( GlobalConstant.JOB_PREFIX_WORKFLOW_SCHEDULE + form.getID(),
        GlobalConstant.JOB_PREFIX_WORKFLOW_SCHEDULE + "GROUP" );
    } catch (SchedulerException se) {
      log.debug("=== Cancel Schedule_Job Fail ===");
      throw new ApplicationException(WorkflowErrorConstant.CANCEL_SCHEDULE_JOB_FAIL);
    }
  }

}
